package com.wsx.ones.gateway.remote.netty;

import com.wsx.ones.gateway.core.config.RouterConfig;
import com.wsx.ones.gateway.remote.RemoteService;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.net.InetSocketAddress;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author wangshuaixin
 * @Title: com.wsx.ones.gateway.remote.netty.NettyRemoteServer
 * @Description: TODO
 * @date 2019/07/17
 */
public class NettyRemoteServer implements RemoteService {

    private static final Logger log = LogManager.getLogger(NettyRemoteServer.class);

    private final NettyServerConfig nettyServerConfig;
    private final RouterConfig routerConfig;

    //netty的启动核心入口
    private final ServerBootstrap serverBootstrap;
    //netty的入口连接池
    private final EventLoopGroup eventLoopGroupSelector;
    //netty的handler的连接池
    private final EventLoopGroup eventLoopGroupBoss;

    private volatile Channel channel;


    public NettyRemoteServer(NettyServerConfig nettyServerConfig,
                             RouterConfig routerConfig) {
        this.nettyServerConfig = nettyServerConfig;
        this.routerConfig = routerConfig;
        this.serverBootstrap = new ServerBootstrap();

        this.eventLoopGroupBoss = new NioEventLoopGroup(BOSS_CORE, new ThreadFactory() {
            private AtomicInteger threadIndex = new AtomicInteger(0);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, String.format("NettyNIOBoss_%d", threadIndex.incrementAndGet()));
            }
        });

        this.eventLoopGroupSelector = new NioEventLoopGroup(nettyServerConfig.getSelectThreads(), new ThreadFactory() {
            private AtomicInteger threadIndex = new AtomicInteger(0);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, String.format("NettyNIOSelector_%d", threadIndex.incrementAndGet()));
            }
        });
    }

    @Override
    public void start() {
        ServerBootstrap childHandler =
                this.serverBootstrap.group(eventLoopGroupBoss, eventLoopGroupSelector)
                        .channel(NioServerSocketChannel.class)
                        .option(ChannelOption.SO_BACKLOG, 1024)
                        .childOption(ChannelOption.SO_KEEPALIVE, true)
                        .handler(new LoggingHandler(LogLevel.INFO))
                        .localAddress(new InetSocketAddress(this.nettyServerConfig.getPort()))
                        .childHandler(new ChannelInitializer<SocketChannel>() {
                            @Override
                            protected void initChannel(SocketChannel socketChannel) throws Exception {
                                socketChannel.pipeline().addLast("http-decoder", new HttpRequestDecoder());
                                socketChannel.pipeline().addLast("http-aggregator", new HttpObjectAggregator(nettyServerConfig.getHttpAggregator()));
                                socketChannel.pipeline().addLast("http-encoder", new HttpResponseEncoder());
                                socketChannel.pipeline().addLast("http-chunked", new ChunkedWriteHandler());
                                socketChannel.pipeline().addLast("nettyServerHandler", new NettyServerHandler(nettyServerConfig, routerConfig));
                            }
                        });


        try {
            ChannelFuture channelFuture = serverBootstrap.bind().sync();
            channelFuture.addListener((future) -> log.info("The start server is success"));
            this.channel = channelFuture.channel();
            channel.closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("netty start error", e);
        }
    }

    @Override
    public void shutdown() {
        if (null != channel) {
            channel.close();
        }
        if (null != eventLoopGroupBoss) {
            eventLoopGroupBoss.shutdownGracefully();
        }
        if (null != eventLoopGroupSelector) {
            eventLoopGroupSelector.shutdownGracefully();
        }
    }
}
